<template>
  <div class="three_load" v-loading="percentageBool" :element-loading-text="nocontainer">
    <div id="container"></div>
  </div>
</template>

<script>
import * as THREE from 'three';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { ArcballControls } from '@/modelTransformation/ArcballControls';

import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js';
let scene = new THREE.Scene();
var controls; // 优化效率

// 顶点着色器
const vertexShader = `
                varying vec2 vUv;
                uniform vec3 lightPosition;
                
                varying vec3 vNormal;
                varying vec3 eyeDirection;
                varying vec3 lightDirection;
                void main() {
                    
                      // vNormal = normalize(modelMatrix * vec4(normal, 0.0)).xyz;
                      // vUv = uv;
                      // gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
                      // vec3 worldPosition = (viewMatrix * modelMatrix * vec4(position, 1.0)).xyz;
                      // vec3 cameraPosition = vec3(0, 0, 0); // 假设这是摄像机位置
                      // eyeDirection = normalize(cameraPosition - worldPosition);
                      // lightDirection = normalize(lightPosition - worldPosition);




                      vNormal = normalize(modelMatrix * vec4(normal, 0.0)).xyz;
                      vUv = uv;
                      gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
                      // vec3 worldPosition = (viewMatrix * modelMatrix * vec4(position, 1.0)).xyz;
                      vec3 worldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
                      eyeDirection = normalize(lightPosition - worldPosition);  
                      lightDirection = normalize(lightPosition - worldPosition);


                      // vNormal = normalize(modelMatrix * vec4(normal, 0.0)).xyz;
                      // vUv = uv;
                      // gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                      // vec3 worldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
                      // eyeDirection = normalize(lightPosition - worldPosition);
                    
                      // lightDirection = normalize(lightPosition - worldPosition);
 
                  
                }
                `;

// 片段着色器（优化后）
const fragmentShader = `
  uniform vec3 lightColor;
  uniform float ambientStrength;
  uniform float diffcoef;  // 漫反射系数（控制颜色明暗）
  uniform float specularStrength;
  uniform sampler2D uTexture;  // 纹理采样器（必须正确传入）
  uniform bool useTexture;     // 新增：是否使用纹理的开关（可选）

  varying vec3 lightDirection;
  varying vec3 vNormal;
  varying vec3 eyeDirection;
  varying vec2 vUv;  // 从顶点着色器传递的UV坐标（必须正确）

  void main() {
    // 1. 计算光照基础成分（环境光+漫反射+镜面反射）
    vec3 ambient = ambientStrength * lightColor;
    
    // 漫反射：基于法线和光线方向的夹角
    vec3 n = normalize(vNormal);       // 法线（必须归一化）
    vec3 l = normalize(lightDirection); // 光线方向（指向光源）
    float diff = max(dot(n, l), 0.0);  // 漫反射因子（0~1）
    vec3 diffuse = diffcoef * diff * lightColor;

    // 镜面反射：基于反射光与视线方向的夹角
    vec3 reflectDir = reflect(-l, n);  // 反射光方向
    vec3 viewDir = normalize(eyeDirection); // 视线方向（从模型到相机）
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); // 高光指数（32更柔和）
    vec3 specular = specularStrength * spec * lightColor;

    // 2. 确定最终颜色：优先使用纹理，无纹理时用淡黄色
    vec3 objColor;
      if (useTexture) { 
      // 若启用纹理，使用纹理颜色
      objColor = texture(uTexture, vUv).rgb;
    } else {
      // 无纹理时，使用淡黄色（QColor(231,223,147)转0-1范围）
      objColor = vec3(231.0/255.0, 223.0/255.0, 147.0/255.0);
    }

    // 3. 合并光照与颜色
    vec3 result = (ambient + diffuse + specular) * objColor;
    gl_FragColor = vec4(result, 1.0); // 输出最终颜色
  }
`;
export default {
  name: 'three_load',
  data() {
    return {
      percentageBool: true,
      fileListArr: [],
      nocontainer: this.$t('oralReport.oralReport_1'),
      camera: null, // 相机
      renderer: null, // 渲染器
      mesh: null, // 网格
      point: null,
      groups: [],
      groups1: [],

      group: {},
      group1: {},
      uploadPercentage: 0,
      planGroup: {},
      isModel: true,

      shaderMaterial: null,
      animationFrameId: '',
    };
  },
  props: {
    fileList: {
      type: Array,
    },
  },
  mounted() {
    this.clearModel();
    this.handleFile();
  },

  methods: {
    resetControls() {
      controls.reset();
    },
    clearModel() {
      if (scene !== null && scene.children.length > 0) {
        scene.children.pop();
        // 必须要清空当前div下的canvas不然canvas会继续叠加
        const domDiv = document.getElementById('container');
        if (!domDiv) {
          domDiv.removeChild(domDiv.firstChild);
        }
        // ...重置相关data变量，例如camera，light...
        scene = null;
        controls = null;
        this.camera = null; // 相机
        this.renderer = null; // 渲染器
        this.mesh = null; // 网格
      }
    },
    initModel() {
      this.group.rotateY(-89.5);
      this.group.rotateX(-165);

      this.group1.rotateY(-89.5);
      this.group1.rotateX(-165);
    },
    handleFile() {
      this.groups = [];

      let promises = this.fileList.map((item) => {
        return new Promise((resolve, reject) => {
          if (item.url && item.url.includes('.stl')) {
            // this.loadSTL(item.url, item.fileName, resolve); // 加载STL模型
            // this.loadSTL(item.url, item.typeName, item.picUrl, resolve); // 加载STL模型
          } else if (item.url && item.url.includes('.ply')) {
            // this.loadPLY(item.url, item.fileName, resolve); // 加载PLY模型
            // this.loadPLY(item.url, item.typeName, item.picUrl, resolve); // 加载PLY模型
          } else if (item.url && item.url.includes('.obj')) {
            this.loadOBJ(false, item.url, item.typeName, item.mtlUrl, item.picUrl, resolve);
            this.loadOBJ(true, item.url, item.typeName, item.mtlUrl, item.picUrl, resolve);
          } else if (item.url && item.url.includes('.blzmesh')) {
            Promise.all([
              this.loadBlzmesh(false, item.url, item.typeName),
              // this.loadBlzmesh(true, item.url, item.typeName),
            ])
              .then(() => resolve()) // 都完成后调用 resolve
              .catch(reject); // 如果有任何一个出错，直接调用 reject
          }
        });
      });

      this.group = new THREE.Object3D();
      this.group1 = new THREE.Object3D();

      // 使用 Promise.all 确保所有模型都加载完毕
      Promise.all(promises).then(() => {
        this.groups.forEach((item) => {
          let tempGroup = new THREE.Group();
          tempGroup.add(item);
          tempGroup['typeName'] = item['typeName'];
          this.group.add(tempGroup);
        });
        console.log('this.groups1--->', this.groups1, this.groups);
        this.groups1.forEach((item) => {
          let tempGroup = new THREE.Group();
          tempGroup.add(item);
          tempGroup['typeName'] = item['typeName'];
          this.group1.add(tempGroup);
        });

        // setTimeout(() => {
        // this.group1 = this.group.clone();
        scene.add(this.group);
        scene.add(this.group1);

        this.group.visible = true;
        this.group1.visible = false;

        this.initModel();

        // // 计算组的中心位置
        let boundingBox = new THREE.Box3().setFromObject(this.group);
        let center = new THREE.Vector3();
        boundingBox.getCenter(center);
        // // 将组居中
        this.group.position.sub(center);

        let boundingBox1 = new THREE.Box3().setFromObject(this.group1);
        let center1 = new THREE.Vector3();
        boundingBox1.getCenter(center1);
        // // 将组居中
        this.group1.position.sub(center1);

        this.nocontainer = this.$t('oralReport.oralReport_1') + '100%';
        // 所有模型加载完成后执行的代码
        this.percentageBool = false;

        let container = document.getElementById('container');

        let canvas = container.querySelector('canvas');
        if (canvas) {
          canvas.style.borderRadius = '10px';
        }

        // }, 500);
      });
      this.init();
    },

    // 加载STL模型
    loadSTL(url, typeName, picUrl, resolve) {
      // const THIS = this
      const loader = new STLLoader();

      loader.load(
        url,
        (geometry) => {
          // 创建材质
          // geometry = new THREE.BoxGeometry(100, 100, 100);
          console.log('创建材质--->', geometry);
          const material = new THREE.MeshLambertMaterial({ color: 0xffffff });
          material.color.setStyle('#b0b0b0'); //模型颜色
          material.side = THREE.DoubleSide;
          this.mesh = new THREE.Mesh(geometry, material);
          // this.mesh.position.set(1, 1, 2)
          this.mesh.rotation.x = -0.5 * Math.PI;
          this.mesh.scale.set(1.2, 1.2, 1.2);
          this[typeName] = this.mesh;
          this.groups.push(this.mesh);
          this.isModel = true;
          resolve();
        },
        (event) => {
          this.calculateProgress(typeName, event);
          // console.log("event---STL------>", event)
        },
        (onerror) => {
          this.percentageBool = false;
          this.isModel = false;
          console.log('err---STL-------->', onerror); // 报错信息在这一行代码
        },
      );
    },
    // 加载PLY模型  ./../../public/jawDie_0_1.ply
    loadPLY(url, typeName, picUrl, resolve) {
      const loader = new PLYLoader();
      // let model = require('../../../public/jawDie_0_1.ply')

      loader.load(
        url,
        (geometry) => {
          if (picUrl) {
            const textureLoader = new THREE.TextureLoader();
            let texture = textureLoader.load(picUrl);
            // geometry.traverse((child) => {
            //   if (child.isMesh) {
            //     child.material.map = texture;
            //   }
            // });
            this.shaderMaterial = new THREE.ShaderMaterial({
              uniforms: {
                lightColor: { value: new THREE.Color(0xffffff) }, // 光源颜色（白色）
                ambientStrength: { value: 0.7 },
                diffcoef: { value: 0.4 }, // 调大可让漫反射更明显（接近真实牙齿）
                specularStrength: { value: 0.1 }, // 镜面反射强度（牙齿高光不宜过强）
                uTexture: { value: texture },
                useTexture: { value: !!texture },
                lightPosition: {
                  value: this.camera.position,
                },
              },
              vertexShader: vertexShader,
              fragmentShader: fragmentShader,
              side: THREE.DoubleSide, // 设置材质为双面
              transparent: true,
              map: texture,
            });
            // geometry.center();
            // 创建网格对象

            // 创建材质
            // const material = new THREE.MeshStandardMaterial({
            //   color: 0xffffff,
            //   flatShading: true,
            //   map: texture,
            // });
            // 设置材质的贴图

            this.mesh = new THREE.Mesh(geometry, this.shaderMaterial);

            this[typeName] = this.mesh;
            this[typeName].traverse((node) => {
              if (node.material) {
                if (node.geometry instanceof THREE.BufferGeometry) {
                  node.geometry.computeVertexNormals();
                  node.geometry.attributes.normal.needsUpdate = true;
                }
              }
            });
            this.groups.push(this.mesh);
            resolve();
          } else {
            const material = new THREE.MeshStandardMaterial({
              color: 0xffffff,
              flatShading: true,
            });
            material.side = THREE.DoubleSide;
            this.mesh = new THREE.Mesh(geometry, material);
            // this.mesh.rotation.x = -0.5 * Math.PI;
            // this.mesh.scale.set(1.2, 1.2, 1.2);
            this[typeName] = this.mesh;
            this.groups.push(this.mesh);
            this.isModel = true;
            resolve();
          }
        },
        (event) => {
          this.calculateProgress(typeName, event);
          // console.log("ply---event--------->", event)
        },
        (onerror) => {
          this.percentageBool = false;
          this.isModel = false;
          console.log('err----------->', onerror); // 报错信息在这一行代码
        },
      );
    },
    // 加载blz模型
    async loadBlzmesh(bool, url, typeName, resolve) {
      return new Promise(async (resolve, reject) => {
        try {
          // 发起 HTTP 请求
          let res = await this.$http({
            method: 'get',
            url: url,
            withCredentials: false,
            responseType: 'arraybuffer',
          });

          // 处理返回的数据
          const data = await this.handleBlzmeshData(res.data);
          const mesh = data.mesh;
          mesh['typeName'] = typeName;

          // 根据 bool 的值选择将 mesh 添加到不同的组中
          if (bool) {
            this.groups1.push(mesh);
          } else {
            this.groups.push(mesh);
          }
          this.isModel = true;
          resolve(); // 加载成功，调用 resolve
        } catch (error) {
          this.isModel = false;
          reject(error); // 如果有错误，调用 reject
        }
      });
    },

    async loadTexture(imgBuffer) {
      const blob = new Blob([imgBuffer], { type: 'image/png' });
      const url = URL.createObjectURL(blob);

      return new Promise((resolve, reject) => {
        const texture = new THREE.TextureLoader().load(
          url,
          () => {
            resolve(texture);
          },
          undefined,
          (error) => {
            reject(error);
          },
        );
      });
    },
    async handleBlzmeshData(arrayBuffer) {
      // 解析 blzmesh 文件数据
      const dataView = new DataView(arrayBuffer);

      let offset = 0;

      // 读取并验证文件标识符 "blz-2.0"
      const identifier = new TextDecoder().decode(new Uint8Array(arrayBuffer, 0, 7));
      if (identifier !== 'blz-2.0') {
        console.error('文件标识符不正确');
        return false;
      }
      offset += 7;

      // 读取缓存数据大小和图像数据大小
      const bufferSize = Number(dataView.getBigInt64(offset, true));
      offset += 8;
      const imgBufferSize = Number(dataView.getBigInt64(offset, true));
      offset += 8;

      // 提取网格数据
      const meshBuffer = arrayBuffer.slice(offset, offset + bufferSize);
      offset += bufferSize;

      // 提取图像数据(如果存在)
      let textureData = null;
      if (imgBufferSize > 0) {
        const imgBuffer = arrayBuffer.slice(offset, offset + imgBufferSize);
        try {
          // 加载纹理
          const texture = await this.loadTexture(imgBuffer);

          textureData = texture;
        } catch (error) {
          console.error('纹理加载失败:', error);
        }
      }

      // 解码网格数据
      try {
        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('https://oss.blzdentalcloud.com/threejs/');

        // 使用 Promise 包装 Draco 解码过程
        let geometry = await new Promise((resolve, reject) => {
          dracoLoader.decodeDracoFile(
            meshBuffer,
            (geometry) => {
              resolve(geometry);
            },
            null,
            null,
            reject,
          );
        });
        // 计算法线
        geometry.computeVertexNormals();
        geometry.attributes.normal.needsUpdate = true;
        // 确保去重后的顶点更新了 UV 信息
        geometry.attributes.uv.needsUpdate = true;
        geometry.attributes.position.needsUpdate = true;
        this.shaderMaterial = new THREE.ShaderMaterial({
          uniforms: {
            lightColor: { value: new THREE.Color(0xffffff) }, // 光源颜色（白色）
            ambientStrength: { value: 0.7 },
            diffcoef: { value: 0.4 }, // 调大可让漫反射更明显（接近真实牙齿）
            specularStrength: { value: 0.1 }, // 镜面反射强度（牙齿高光不宜过强）
            uTexture: { value: textureData },
            useTexture: { value: !!textureData },
            lightPosition: {
              value: this.camera.position,
            },
          },
          vertexShader: vertexShader,
          fragmentShader: fragmentShader,
          side: THREE.DoubleSide, // 设置材质为双面
          transparent: true,
          map: textureData,
        });

        // const mesh = new THREE.Mesh(geometry, material);
        const mesh = new THREE.Mesh(geometry, this.shaderMaterial);

        return {
          mesh,
          geometry,
        };
      } catch (error) {
        console.error('网格解码失败:', error);
        return null;
      }
    },
    // 加载OBJ模型(bool确定是否是平铺)
    loadOBJ(bool, url, typeName, mtlUrl, picUrl, resolve) {
      const mtlLoader = new MTLLoader();
      const objLoader = new OBJLoader();
      if (mtlUrl && picUrl) {
        mtlLoader.load(mtlUrl, (materials) => {
          // 将材质库中的材质替换成Three.js的材质
          materials.preload();
          objLoader.setMaterials(materials);
          objLoader.load(
            url,
            (geometry) => {
              // 如果是平铺的话让上颚夏尔位置一样
              if (bool) {
                // 获取模型的包围盒
                const box = new THREE.Box3().setFromObject(geometry);
                // 计算模型的中心位置
                const center = new THREE.Vector3();
                box.getCenter(center);

                geometry.position.copy(center).multiplyScalar(-1);
              }
              geometry['typeName'] = typeName;
              this[typeName] = geometry;
              if (picUrl) {
                const textureLoader = new THREE.TextureLoader();
                let texture = textureLoader.load(picUrl);

                this.shaderMaterial = new THREE.ShaderMaterial({
                  uniforms: {
                    lightColor: { value: new THREE.Color(0xffffff) }, // 光源颜色（白色）
                    ambientStrength: { value: 0.7 },
                    diffcoef: { value: 0.4 }, // 调大可让漫反射更明显（接近真实牙齿）
                    specularStrength: { value: 0.1 }, // 镜面反射强度（牙齿高光不宜过强）
                    uTexture: { value: texture },
                    useTexture: { value: !!texture },
                    lightPosition: {
                      value: this.camera.position,
                    },
                  },
                  vertexShader: vertexShader,
                  fragmentShader: fragmentShader,
                  side: THREE.DoubleSide, // 设置材质为双面
                  transparent: true,
                  map: texture,
                });
                // 设置材质的贴图
                geometry.traverse((child) => {
                  if (child.isMesh) {
                    child.material.map = texture;
                  }
                });
                this[typeName].traverse((node) => {
                  if (node.material) {
                    if (node.geometry instanceof THREE.BufferGeometry) {
                      node.geometry.computeVertexNormals();
                      node.geometry.attributes.normal.needsUpdate = true;
                    }
                    node.material = this.shaderMaterial;
                    node.name = 'obj';
                  }

                  // node.name = 'myCube'; // 设置名称
                });
              } else {
                this[typeName].traverse((node) => {
                  if (node.material) {
                    node.material.transparent = true;
                    this.materialData = node.material;
                    this.materialData.side = THREE.DoubleSide;
                  }
                });
              }

              // geometry.position.set(1,1,1);

              if (bool) {
                this.groups1.push(geometry);
              } else {
                this.groups.push(geometry);
              }
              resolve();
            },
            (event) => {
              this.calculateProgress(typeName, event);
            },
            (onerror) => {
              console.log('err----------->', onerror);
            },
          );
        });
      } else {
        objLoader.load(
          url,
          (geometry) => {
            // 如果是平铺的话让上颚夏尔位置一样
            if (bool) {
              // 获取模型的包围盒
              const box = new THREE.Box3().setFromObject(geometry);
              // 计算模型的中心位置
              const center = new THREE.Vector3();
              box.getCenter(center);

              geometry.position.copy(center).multiplyScalar(-1);
            }

            geometry['typeName'] = typeName;

            // geometry.position.set(1,1,1);

            this[typeName] = geometry;
            this[typeName].traverse((node) => {
              if (node.material) {
                node.material.transparent = true;
                this.materialData = node.material;
                this.materialData.side = THREE.DoubleSide;
              }
            });
            if (bool) {
              this.groups1.push(geometry);
            } else {
              this.groups.push(geometry);
            }
            resolve();
          },
          (event) => {
            this.calculateProgress(typeName, event);
          },
          (onerror) => {
            console.log('err----------->', onerror);
          },
        );
      }
    },
    modelTiling() {
      this.group.visible = false;
      this.group1.visible = true;

      this.group1.children.forEach((item) => {
        if (item.typeName.indexOf('upper') !== -1) {
          // 上颚

          item.rotateY(23.5);
          item.position.set(item.position.x - 40, item.position.y, item.position.z + 34);
        } else {
          item.rotateY(-23.5);
          item.position.set(item.position.x - 20, item.position.y, item.position.z - 30);
          // 下颚
        }
      });
    },
    // 定义一个方法来计算模型加载进度
    calculateProgress(typeName, event) {
      let plan = 0;
      if (this.planGroup.hasOwnProperty(typeName)) {
        this.planGroup[typeName] = event.loaded / event.total;
      } else {
        this.planGroup[typeName] = '';
      }
      for (let key in this.planGroup) {
        plan += Number(this.planGroup[key]);
      }
      this.uploadPercentage = ((plan / this.fileListArr.length) * 100).toFixed(2);
      if (Number(this.uploadPercentage) > 99) {
        this.uploadPercentage = 99;
      }

      this.nocontainer = this.$t('oralReport.oralReport_1') + this.uploadPercentage + '%';
    },

    init() {
      if (scene == null || scene.children.length < 3) {
        scene = new THREE.Scene();
        this.createLight(); // 创建光源
      }
      this.createCamera(); // 创建相机
      this.createRender(); // 创建渲染器
      this.createControls(); // 创建控件对象
      this.render(); // 渲染
    },
    // 创建光源
    createLight() {
      // // 环境光
      var ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
      scene.add(ambientLight);
      // // 镜面光
      // this.spotLight = new THREE.SpotLight(0xffffff, 1);
      // scene.add(this.spotLight);
      // 点光源
      this.point = new THREE.PointLight(0xffffff);
      this.point.position.set(0, 0, 200); // 点光源位置
      scene.add(this.point);
      // 环境光
      // var ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
      // scene.add(ambientLight);
    },
    createCamera() {
      const element = document.getElementById('container');
      const width = element.clientWidth; // 窗口宽度
      const height = element.clientHeight; // 窗口高度
      const k = width / height; //窗口宽高比
      const s = 65; //三维场景显示范围控制系数，系数越大，显示的范围越大
      // this.camera = new THREE.PerspectiveCamera(35, k, 0.1, 1000);

      this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 0.1, 1000);
      this.camera.position.set(0, 0, 200);
      // this.camera.position.set(0, 0, 0);
      this.camera.lookAt(new THREE.Vector3(0, 0, 0));
    },
    // 创建渲染器
    createRender() {
      const element = document.getElementById('container');
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: false,
        preserveDrawingBuffer: true,
      });

      this.renderer.outputEncoding = THREE.sRGBEncoding;
      this.renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
      this.renderer.setClearColor('rgb(255,255,255)', 0.5); // 设置背景颜色

      element.appendChild(this.renderer.domElement);
    },

    // 创建控件对象
    createControls() {
      controls = new ArcballControls(this.camera, this.renderer.domElement, scene);
      controls.setGizmosVisible(false);
      controls.enablePan = false;
      controls.dampingFactor = 100;
      controls.unsetMouseAction('FOV', 0);
      controls.setMouseAction('ZOOM', 1);
      controls.setMouseAction('ROTATE', 2);
      controls.unsetMouseAction('0');
    },
    render() {
      this.animationFrameId = requestAnimationFrame(this.render);
      if (controls) {
        controls.update();
      }
      if (this.camera.position) {
        var vector = this.camera.position.clone();
      }

      this.point.position.set(vector.x, vector.y, vector.z); // 点光源位置
      this.renderer.render(scene, this.camera);
    },
  },
  beforeDestroy() {
    // 取消动画循环
    cancelAnimationFrame(this.animationFrameId);
    // 清理渲染器
    if (this.renderer) {
      this.renderer.dispose();
      this.renderer.forceContextLoss();
    }

    // 清理场景中的对象
    scene.traverse((object) => {
      if (object.geometry) object.geometry.dispose();
      if (object.material) {
        if (object.material.map) object.material.map.dispose();
        object.material.dispose();
      }
      if (object instanceof THREE.Light) {
        object.dispose(); // 销毁光源
      }
    });
    scene = null;
    this.camera = null;
    controls = null;
  },
};
</script>

<style lang="scss" scoped>
.three_load {
  width: 100%;
  height: 100%;
  border-radius: 10px;

  ::v-deep .el-loading-mask {
    border-radius: 10px;
  }

  #container {
    width: 100%;
    height: 100%;
    border-radius: 10px;
  }
}
</style>
